main();

//
// 从这里开始
//
function main() {
  const canvas = document.querySelector('#glcanvas');
  const gl = canvas.getContext('webgl');

  // 如果没有GL上下文，立即放弃

  if (!gl) {
    alert('无法初始化WebGL。您的浏览器或计算机可能不支持它。');
    return;
  }

  // 顶点着色器程序

  const vsSource = `
    attribute vec4 aVertexPosition;
    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;
    void main() {
      gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
    }
  `;

  // 片段着色器程序

  const fsSource = `
    void main() {
      gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
    }
  `;

  // 初始化着色器程序；这是为顶点和其他元素设置光照效果的地方。
  const shaderProgram = initShaderProgram(gl, vsSource, fsSource);


  /**
   * 存储着程序信息的对象。
   * @typedef {Object} ProgramInfo
   * @property {WebGLProgram} program - 着色器程序对象。
   * @property {Object} attribLocations - 存储着属性位置的对象。
   * @property {number} attribLocations.vertexPosition - 顶点位置属性的位置。
   * @property {Object} uniformLocations - 存储着统一变量位置的对象。
   * @property {WebGLUniformLocation} uniformLocations.projectionMatrix - 投影矩阵统一变量的位置。
   * @property {WebGLUniformLocation} uniformLocations.modelViewMatrix - 模型视图矩阵统一变量的位置。
   */

  // 收集使用着色器程序所需的所有信息。
  // 查找我们的着色器程序中用于 aVertexPosition 的属性，并查找统一位置。
  const programInfo = {
    program: shaderProgram,
    attribLocations: {
      vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
    },
    uniformLocations: {
      projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
      modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
    },
  };

  // 这里是我们调用构建所有要绘制的对象的例程。
  const buffers = initBuffers(gl);

  // 绘制场景
  drawScene(gl, programInfo, buffers);
}

//
// initBuffers
//
// 初始化我们需要的缓冲区。对于这个演示，我们只有一个对象 - 一个简单的二维正方形。
//
function initBuffers(gl) {

  // 为正方形的位置创建一个缓冲区。

  const positionBuffer = gl.createBuffer();

  // 将positionBuffer选择为从此处开始应用缓冲区操作的缓冲区。

  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

  // 现在为正方形创建一个位置数组。

  const positions = [
    1.0, 1.0,
    -1.0, 1.0,
    1.0, -1.0,
    -1.0, -1.0,
  ];

  // 现在将位置列表传递给WebGL以构建形状。我们通过从JavaScript数组创建一个Float32Array，然后使用它来填充当前缓冲区来实现这一点。

  gl.bufferData(gl.ARRAY_BUFFER,
    new Float32Array(positions),
    gl.STATIC_DRAW);

  return {
    position: positionBuffer,
  };
}

//
// 绘制场景。
//
function drawScene(gl, programInfo, buffers) {
  gl.clearColor(0, 0, 0, 0);  // 清除为黑色，完全不透明
  // gl.clearDepth(1.0);                 // 清除所有内容
  gl.enable(gl.DEPTH_TEST);           // 启用深度测试
  gl.depthFunc(gl.LEQUAL);            // 近处物体遮挡远处物体

  // 在开始绘制之前清除画布。

  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  // 创建透视矩阵，这是一种特殊的矩阵，用于模拟相机的透视失真。
  // 我们的视场角度为45度，宽度/高度比与画布的显示大小相匹配，
  // 我们只想看到距离相机0.1单位和100单位之间的物体。

  const fieldOfView = 45 * Math.PI / 180;   // 以弧度为单位
  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const zNear = 0.1;
  const zFar = 100.0;
  const projectionMatrix = mat4.create();

  // 注意：glmatrix.js总是将第一个参数作为接收结果的目标。
  mat4.perspective(projectionMatrix,
    fieldOfView,
    aspect,
    zNear,
    zFar);

  // 将绘制位置设置为“identity”点，即场景的中心。
  const modelViewMatrix = mat4.create();

  // 现在将绘制位置移动到我们要开始绘制正方形的位置。

  mat4.translate(modelViewMatrix,     // 目标矩阵
    modelViewMatrix,     // 要平移的矩阵
    [0.0, 0.0, -7.0]);  // 平移量

  // 告诉WebGL如何从位置缓冲区中提取位置到vertexPosition属性。
  {
    const numComponents = 2;
    const type = gl.FLOAT;
    const normalize = false;
    const stride = 0;
    const offset = 0;
    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
    gl.vertexAttribPointer(
      programInfo.attribLocations.vertexPosition,
      numComponents,
      type,
      normalize,
      stride,
      offset);
    gl.enableVertexAttribArray(
      programInfo.attribLocations.vertexPosition);
  }

  // 告诉WebGL在绘制时使用我们的程序

  gl.useProgram(programInfo.program);

  // 设置着色器统一变量

  gl.uniformMatrix4fv(
    programInfo.uniformLocations.projectionMatrix,
    false,
    projectionMatrix);
  gl.uniformMatrix4fv(
    programInfo.uniformLocations.modelViewMatrix,
    false,
    modelViewMatrix);

  {
    const offset = 0;
    const vertexCount = 4;
    gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
  }
}


/**
 * 初始化着色器程序
 * 
 * @param {WebGLRenderingContext} gl - WebGL渲染上下文
 * @param {string} vsSource - 顶点着色器源代码
 * @param {string} fsSource - 片元着色器源代码
 * @returns {WebGLProgram} - 创建的着色器程序
 */
//
// 初始化着色器程序，以便WebGL知道如何绘制我们的数据
//
function initShaderProgram(gl, vsSource, fsSource) {
  const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
  const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

  // 创建着色器程序

  const shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  // 如果创建着色器程序失败，弹出警告

  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    alert('无法初始化着色器程序：' + gl.getProgramInfoLog(shaderProgram));
    return null;
  }

  return shaderProgram;
}

//
// 创建给定类型的着色器，上传源代码并编译它。
//
function loadShader(gl, type, source) {
  // 创建着色器对象

  const shader = gl.createShader(type);

  // 将源代码发送到着色器对象

  gl.shaderSource(shader, source);

  // 编译着色器程序

  gl.compileShader(shader);

  // 检查它是否成功编译

  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    alert('编译着色器时发生错误：' + gl.getShaderInfoLog(shader));
    gl.deleteShader(shader);
    return null;
  }

  return shader;
}
